package fifis.network.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * Class that handles the messages received from the client
 * 
 * @author porfiriopartida
 */
public class ClientHandle extends Thread {

    /** 
     * Id (or count) of current client
     */
    int clientId;
    /**
     * Client's nickname
     */
    String nickname;
    /**
     * Default prefix for just connected users
     */
    public static final String PREFIX = "user-";
    /**
     * How many instances of clients were running since server started
     */
    private static int CLIENT_COUNT = 0;
    /**
     * Input to read from client
     */
    private BufferedReader in;
    /**
     * Output to write to the client
     */
    private PrintWriter out;
    /**
     * Socket instance that determines client's connection
     */
    Socket socket;
    /**
     * ServerSocket instance for this handling
     */
    FifisServer server;
    /**
     * Wether or not the client has identified itself (for login uses)
     */
    private boolean identified = false;

    /**
     * Creates a new ClientHandle instance determined by ServerSocket instance and Client's Socket instance
     * 
     * @param server ServerSocket for this handling
     * @param socket Client's Socket connection
     */
    public ClientHandle(FifisServer server, Socket socket) {
        this(server, socket, CLIENT_COUNT++);
    }

    /**
     * Creates a new ClientHandle instance determined by ServerSocket instance and Client's Socket instance
     * 
     * @param server ServerSocket for this handling
     * @param socket Client's Socket connection
     * @param id Client's id 
     */
    public ClientHandle(FifisServer server, Socket socket, int id) {
        this(server, socket, id, PREFIX + id);
    }

    /**
     * Creates a new ClientHandle instance determined by ServerSocket instance and Client's Socket instance
     * 
     * @param server ServerSocket for this handling
     * @param socket Client's Socket connection
     * @param id Client's id
     * @param nickname Client's nickname 
     */
    public ClientHandle(FifisServer server, Socket socket, int id, String nickname) {
        this.clientId = id;
        this.nickname = nickname;
        this.socket = socket;
        this.server = server;
    }

    /**
     * Gets the client's id, it could be useful if clients are being stored in arrays or other non movable index, anyway you can set it manually
     * 
     * @return the client's id
     */
    public int getClientId() {
        return clientId;
    }
    
    /**
     * Since ClientHandle extends Thread, this method will retrieve Thread id instead of Client id, you may want to use
     * getClientId() instead.
     * <br />
     * 
     * Returns the identifier of this Thread.  The thread ID is a positive
     * <tt>long</tt> number generated when this thread was created.
     * The thread ID is unique and remains unchanged during its lifetime.
     * When a thread is terminated, this thread ID may be reused.
     *
     * @return this thread's ID.
     * @since 1.5
     */
    @Override
    public long getId(){
        return super.getId();
    }

    /**
     * Sets the client's nickname
     * @param nickname 
     */
    public void setNickname(String nickname) {
        System.err.println(this.nickname + " updated to " + nickname);
        this.nickname = nickname;
    }

    public void setIdentified(boolean identified) {
        this.identified = identified;
    }

    public boolean getIsIdentified() {
        return identified;
    }

    public String getNickname() {
        return nickname;
    }

    public Socket getSocket() {
        return socket;
    }

    private void initChannels() {
        //in.readLine();
        //out.println("msg");
        try {
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
        } catch (IOException ex) {
            System.err.println("Error while initializing channels " + ex.getMessage());
            //Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized void finalizeClient() {
        if (socket == null || socket.isClosed()) {
            return;
        }
        try {
            socket.close();
        } catch (IOException ex) {
            System.err.println("Error while closing socket " + ex.getMessage());
            //Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void send(String msg) {
        try {
            out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private void handle(String line) {
        if (line != null) {
            server.handle(clientId, line);
        }
    }
    private String group = "N/A";

    public void setGroup(String group) {
        this.group = group;
    }
    @Deprecated
    public void setProject(String project) {
        this.group = project;
    }

    public String getGroup() {
        return group;
    }
    /**
     * Use getGroup instead
     * 
     * @return the project name
     * @deprecated
     */
    @Deprecated
    public String getProject() {
        return group;
    }
    
    @Override
    public void run() {
        initChannels();
        
        server.clientConnected(this);
        
        while (true) {
            String line;
            try {
                line = in.readLine();
                
                if (line == null) {
                    break;
                } else {
                    handle(line);
                }
            } catch (IOException ex) {
                //server.clientDisconnected(this);
                //System.err.println("Client disconnected: "+clientId+" :: "+ex.getMessage());
                //Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex);
                break;
            }
        }//end while
        //System.err.println("Closing client: "+clientId);
        finalizeClient();
        //server.serverReporter.serverReport(FifisServer.UPDATE_CLIENTS);
        FifisServer.clients.remove(getClientId());
        
        server.clientDisconnected(this);
    }
}
